package dq.study.minimvc;

import com.fasterxml.jackson.databind.ObjectMapper;
import dq.study.minimvc.annotation.Listener;
import dq.study.minimvc.context.ContextLoader;
import dq.study.minimvc.handler.RequestMappingHandlerAdapter;
import dq.study.minimvc.handler.RequestMappingHandlerMapping;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class DispatcherServlet extends HttpServlet {

    private List<HandlerMapping> handlerMappings;

    private List<HandlerAdapter> handlerAdapters;

    @Override
    public void init(ServletConfig config) throws ServletException {
        initStrategies();
        super.init(config);
    }

    private void initStrategies() {
        initHandlerMapping();
        initHandlerAdapter();
    }

    private void initHandlerMapping() {
        handlerMappings = new ArrayList<>();
        handlerMappings.add(new RequestMappingHandlerMapping());
    }

    private void initHandlerAdapter() {
        handlerAdapters = new ArrayList<>();
        handlerAdapters.add(new RequestMappingHandlerAdapter());
    }


    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //super.service(req, resp);
        try {
            doDispatch(req,resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        Exception dispatchException = null;
        Object result = null;

        try {
            mappedHandler = getHandler(processedRequest);
            mappedHandler.addInterceptor(new SystemListener());
            List<HandlerInterceptor> interceptors = (List<HandlerInterceptor>) ContextLoader.getInstance().getHandlers().get("listener");
            if (interceptors!=null && interceptors.size()>0) mappedHandler.addInterceptors(interceptors);
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }
            result = ha.handle(request,response,mappedHandler.getHandler());
            mappedHandler.applyPostHandle(request,response);
        } catch (Exception e) {
            dispatchException = e;
            e.printStackTrace();
        }finally {
            processDispatchResult(processedRequest,response,mappedHandler,result,dispatchException);
        }


    }

    private void processDispatchResult(HttpServletRequest processedRequest, HttpServletResponse response, HandlerExecutionChain mappedHandler, Object result, Exception dispatchException) {
        try {
            if (processedRequest.getRequestURI().equals(processedRequest.getContextPath()+"/")){
                ObjectMapper mapper = new ObjectMapper();
                response.getWriter().write("<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><title>Title</title></head><body><h1>HELLO MINIMVC</h1></body></html>");
            }else {
                response.setHeader("Content-type", "text/html;charset=UTF-8");
                response.setCharacterEncoding("UTF-8");
                ObjectMapper mapper = new ObjectMapper();
                response.getWriter().write(mapper.writeValueAsString(result));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            for (HandlerMapping mapping : this.handlerMappings) {
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            for (HandlerAdapter adapter : this.handlerAdapters) {
                if (adapter.supports(handler)) {
                    return adapter;
                }
            }
        }
        throw new ServletException("No adapter for handler [" + handler +
                "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    }
}
